//if connected & if tables do not exist, create them and insert example rows
if (!this.dbFunc.tableExists('functions')) {
var aQ = this.getFuncQueries();
this.dbFunc.executeTransaction(aQ);
}
if (!this.dbFunc.tableExists('aggregateFunctions')) {
var aQ = this.getAggFuncQueries();
this.dbFunc.executeTransaction(aQ);
}
return true;
}
} catch (e) {
Components.utils.reportError('Failed to open a connection to UDF database\n' + (fUserFile != null)?'db: user selected':'db: supplied');
}
this.dbFunc = null;
return false;
},
getFuncQueries: function() {
var aSql = [];
aSql.push('DROP TABLE IF EXISTS "functions";');
aSql.push('CREATE TABLE "functions" ("name" TEXT PRIMARY KEY NOT NULL, "body" TEXT NOT NULL, "argLength" INTEGER, "aggregate" INTEGER NOT NULL DEFAULT 0, "enabled" INTEGER NOT NULL DEFAULT 1, "extraInfo" TEXT);');
aSql.push('INSERT INTO "functions" VALUES("regexp",\'var regExp = new RegExp(aValues.getString(0));\nvar strVal = new String(aValues.getString(1));\n\nif (strVal.match(regExp)) return 1;\nelse return 0;\',2,0,1,NULL);');
aSql.push('INSERT INTO "functions" VALUES("addAll","var sum = 0;\nfor (var j = 0; j < aValues.numEntries; j++) {\n sum += aValues.getInt32(j);\n}\nreturn sum;",-1,0,1,NULL);');
aSql.push('INSERT INTO "functions" VALUES("joinValues","var valArr = [];\n\nfor (var j = 0; j < aValues.numEntries; j++) {\n switch (aValues.getTypeOfIndex(j)) {\n case 0: //NULL\n valArr.push(null);\n break;\n case 1: //INTEGER\n valArr.push(aValues.getInt64(j));\n break;\n case 2: //FLOAT\n valArr.push(aValues.getDouble(j));\n break;\n case 3: //TEXT\n default:\n valArr.push(aValues.getString(j));\n }\n}\nreturn valArr.join(\',\');",-1,0,1,NULL);');
return aSql;
},
getAggFuncQueries: function() {
var aSql = [];
aSql.push('DROP TABLE IF EXISTS "aggregateFunctions";');
aSql.push('CREATE TABLE "aggregateFunctions" ("name" TEXT PRIMARY KEY NOT NULL, "argLength" INTEGER, "onStepBody" TEXT, "onFinalBody" TEXT, "enabled" INTEGER NOT NULL DEFAULT 1, "extraInfo" TEXT);');
aSql.push('INSERT INTO "aggregateFunctions" ("name", "argLength", "onStepBody", "onFinalBody", "enabled", "extraInfo") VALUES("stdDev", 1, "this._store.push(aValues.getInt32(0));", "var iLength = this._store.length;\nlet total = 0;\nthis._store.forEach(function(elt) { total += elt });\nlet mean = total / iLength;\nlet data = this._store.map(function(elt) {\n let value = elt - mean;\n return value * value;\n});\ntotal = 0;\ndata.forEach(function(elt) { total += elt });\nthis._store = [];\nreturn Math.sqrt(total / iLength);",1,NULL);');
return aSql;
},
close: function() {
//close connection to udf db
try {
if (this.dbFunc != null)
this.dbFunc.closeConnection();
} catch (e) {
sm_log('Failed to close the connection to UDF database');
var dir = SmGlobals.chooseDirectory("Choose location of user-defined functions database (smFunctions.sqlite)...");
if (dir == null) {
alert("Please choose a directory before proceeding.\nIf you already have smFunctions.sqlite file, then choose the directory where it is located.\nIf you do NOT have an existing smFunctions.sqlite file, then one will be created in the directory you choose.\nThe chosen location should have read/write permissions.");
sm_notify('udfNotifyBox', 'New aggregate function added: ' + sName + '. Press "Reload Functions" button to access this function in SQL statements.', 'info');
this.dbFunc.selectQuery("SELECT name, onStepBody, onFinalBody, argLength, enabled FROM aggregateFunctions WHERE name = '" + sFuncName + "' ORDER BY name");
records = this.dbFunc.getRecords();
} catch (e) {
sm_log(e.message);
return false;
}
var sTxt = [], sBody = [];
for (var i in records) {
sTxt.push('// name = ' + records[i][0]);
sTxt.push('// argLength = ' + records[i][3]);
sTxt.push('// enabled = ' + records[i][4]);
sBody.push('var objectForAggregateFunction = {');
sBody.push('_store: [], //for storing values which can be used in onFinal()');
sBody.push('onStep: function (aValues) {//called for each row');
sBody.push(records[i][1]);
sBody.push('},');
sBody.push('onFinal: function () {//called at the end');
this.dbFunc.selectQuery("SELECT name, onStepBody, onFinalBody, argLength, enabled FROM aggregateFunctions WHERE name = '" + sFuncName + "' ORDER BY name");
//of course, we cannot proceed without a db connection
if (this.dbFunc == null)
return;
var allUdf = [];
this.dbFunc.selectQuery('SELECT name, body, argLength FROM functions WHERE enabled = 1 AND aggregate = 0');
var records = this.dbFunc.getRecords();
for (var i in records) {
try {
var func = new Function("aValues", records[i][1]);
var udf = {fName: records[i][0], fLength: records[i][2], onFunctionCall: func};
allUdf.push(udf);
} catch (e) {
sm_log("Failed to create function: " + records[i][0]);
}
}
return allUdf;
},
getAggregateFunctions: function() {
//of course, we cannot proceed without a db connection
if (this.dbFunc == null)
return;
var allUdf = [];
this.dbFunc.selectQuery('SELECT name, argLength, onStepBody, onFinalBody FROM aggregateFunctions WHERE enabled = 1');
var records = this.dbFunc.getRecords();
for (var i in records) {
try {
var objAggFunc = {
_store: [],
onStep: new Function("aValues", records[i][2]),
onFinal: new Function(records[i][3])
};
var udf = {fName: records[i][0], fLength: records[i][1], objFunc: objAggFunc};
allUdf.push(udf);
} catch (e) {
sm_log("Failed to create function: " + records[i][0]);
}
}
return allUdf;
},
showHelp: function(sArg) {
switch (sArg) {
case 'newFunctionArgLength':
smPrompt.alert(null, sm_getLStr("extName"), 'The number of arguments that the function will accept should be an integer.\n-1 means unlimited number of arguments.');
break;
case 'newFunctionBody':
smPrompt.alert(null, sm_getLStr("extName"), 'Write the function body without braces.\nThe argument to the function is "aValues" which can be used within the function body as in the example functions which you can see under the Simple Functions tab.');
break;
case 'newFunctionOnStepBody':
smPrompt.alert(null, sm_getLStr("extName"), 'Write the function body without braces.\nThe argument to the function is "aValues" which can be used within the function body as in the example functions which you can see under the Aggregate Functions tab.\nAny values you need to store for use in onFinal() can be stored in this._store which is initialized as an empty array');
break;
case 'newFunctionOnFinalBody':
smPrompt.alert(null, sm_getLStr("extName"), 'Write the function body without braces.\nThis function takes no arguments. See an example under the Aggregate Functions tab.\nYou can use this._store for computation in this function after you have stored values in it in the onStep() function.');